Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(organizations): create endpoints to handle organization invitations TASK-969 #5395

Merged
merged 14 commits into from
Jan 24, 2025

Conversation

rajpatel24
Copy link
Contributor

@rajpatel24 rajpatel24 commented Dec 20, 2024

🗒️ Checklist

  1. run linter locally
  2. update all related docs (API, README, inline, etc.), if any
  3. draft PR with a title <type>(<scope>)<!>: <title> TASK-1234
  4. tag PR: at least frontend or backend unless it's global
  5. fill in the template below and delete template comments
  6. review thyself: read the diff and repro the preview as written
  7. open PR & confirm that CI passes
  8. request reviewers, if needed
  9. delete this section before merging

📣 Summary

Implemented endpoints for organization invitations, allowing organization owners to invite existing users or unregistered users to join their organization. The invitee can either accept or decline the invitation. If the invitee accepts, their assets will be transferred to the organization.

📖 Description

  • Organization owners can send invitations to users (both registered and unregistered) via email or username.
  • The invitee can accept or decline the invitation. If accepted, the invitee's assets will be transferred to the organization owner.

POST https://[kpi]/api/v2/organizations/org_12345/invites/

  • Create organization invites for registered and unregistered users.
  • Set the role for which the user is being invited - (Choices: member, admin). Default is member.

Payload:

{
    "invitees": ["demo14", "[email protected]", "[email protected]"],
    "role": "admin"
}

Response:

[
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "demo14"
    },
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/6746121a-7a87-4c2d-9994-85e38d8cff65/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "demo13"
    },
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/2af706a9-4f67-4145-a0d3-8d66fbc77a19/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "[email protected]"
    }
]

GET https://[kpi]/api/v2/organizations/org_12345/invites/

Response:

{
    "count": 3,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/6746121a-7a87-4c2d-9994-85e38d8cff65/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "demo13"
        },
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "demo14"
        },
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/2af706a9-4f67-4145-a0d3-8d66fbc77a19/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "[email protected]"
        }
    ]
}

PATCH https://[kpi]/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/

  • Update an organization invite to accept, decline, cancel, expire, or resend.

Payload:

{
    "status": "accepted"
}

Response:

{
    "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
    "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
    "status": "accepted",
    "invitee_role": "admin",
    "created": "2025-01-06T13:01:40Z",
    "modified": "2025-01-06T13:01:40Z",
    "invitee": "demo14"
}

DELETE https://[kpi]/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/

  • Organization owner or admin can delete an organization invite.

Response: 204

@rajpatel24 rajpatel24 force-pushed the task-969-create-endpoints-to-handle-org-invitations branch from 092d932 to 64a2b55 Compare December 30, 2024 15:46
@rajpatel24 rajpatel24 changed the title Create endpoints to handle organization invitations feat(organizations): create endpoints to handle organization invitations Dec 30, 2024
@rajpatel24 rajpatel24 marked this pull request as ready for review December 30, 2024 16:14
@rajpatel24 rajpatel24 removed the request for review from jnm December 30, 2024 16:14
Copy link

Copy link

Copy link

@rajpatel24 rajpatel24 force-pushed the task-969-create-endpoints-to-handle-org-invitations branch from 64a2b55 to 1139076 Compare January 1, 2025 15:45
@rajpatel24 rajpatel24 self-assigned this Jan 1, 2025
@rajpatel24 rajpatel24 force-pushed the task-969-create-endpoints-to-handle-org-invitations branch from 1139076 to c28ad37 Compare January 2, 2025 14:20
@magicznyleszek
Copy link
Member

magicznyleszek commented Jan 4, 2025

@rajpatel24 Wuld that whole invite object be present in /api/v2/organizations/:organization_id/members/ in the invite property? And I see you have pending status in there, and is this the same as sent?

@rajpatel24
Copy link
Contributor Author

@rajpatel24 Wuld that whole invite object be present in /api/v2/organizations/:organization_id/members/ in the invite property? And I see you have pending status in there, and is this the same as sent?

@magicznyleszek No, the invite object would not be present there. I don't think it's necessary since /api/v2/organizations/:organization_id/members/ is designed to list the members who are already part of the organization - those who have accepted the invitations and are now part of it.

The pending status is the same as sent. When the organization owner sends an invitation, the status of the invite will be set to pending.

@rajpatel24 rajpatel24 force-pushed the task-969-create-endpoints-to-handle-org-invitations branch from 31ac077 to ea9a6bd Compare January 6, 2025 13:15
@rajpatel24 rajpatel24 force-pushed the task-969-create-endpoints-to-handle-org-invitations branch from 32b1431 to 5394358 Compare January 7, 2025 15:37
kobo/apps/organizations/serializers.py Show resolved Hide resolved
kobo/apps/organizations/tasks.py Outdated Show resolved Hide resolved
kobo/apps/organizations/models.py Show resolved Hide resolved

{% trans "All projects, submissions, data storage, transcription and translation usage for their projects will be transferred to you." %}

{% trans "Note: You will continue to have permissions to manage these projects until the user permissions are changed." %}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this sentence should be here since that's the sender who receives this message.
(Please fix HTML template too)

kobo/apps/organizations/constants.py Outdated Show resolved Hide resolved
kobo/apps/organizations/models.py Outdated Show resolved Hide resolved
kobo/apps/organizations/models.py Outdated Show resolved Hide resolved
kobo/apps/organizations/permissions.py Show resolved Hide resolved
kobo/apps/organizations/tasks.py Outdated Show resolved Hide resolved
kobo/apps/organizations/views.py Show resolved Hide resolved
kobo/apps/organizations/views.py Show resolved Hide resolved
@@ -365,21 +376,26 @@ def send_invite_email(self):
email_message = EmailMessage(
to=to_email,
subject=t(
f"You're invited to join {organization_name}'s organization"
),
f"You're invited to join ##organization_name## organization"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does not need to be an f-string anymore

f"You're invited to join {organization_name}'s organization"
),
f"You're invited to join ##organization_name## organization"
).replace('##organization_name##', organization_name),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could use your utility replace_placeholders()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also I have moved the utility function to a new file, as it is a generic utility not specific to the organizations app.

kobo/apps/organizations/serializers.py Outdated Show resolved Hide resolved
@noliveleger noliveleger changed the title feat(organizations): create endpoints to handle organization invitations feat(organizations): create endpoints to handle organization invitations TASK-969 Jan 23, 2025
Copy link
Contributor

@noliveleger noliveleger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rajpatel24 rajpatel24 merged commit ea727fb into main Jan 24, 2025
4 checks passed
@rajpatel24 rajpatel24 deleted the task-969-create-endpoints-to-handle-org-invitations branch January 24, 2025 07:55
rajpatel24 added a commit that referenced this pull request Jan 28, 2025
…ons TASK-969 (#5395)

### 📣 Summary
Implemented endpoints for organization invitations, allowing
organization owners to invite existing users or unregistered users to
join their organization. The invitee can either accept or decline the
invitation. If the invitee accepts, their assets will be transferred to
the organization.

### 📖 Description

- Organization owners can send invitations to users (both registered and
unregistered) via email or username.
- The invitee can accept or decline the invitation. If accepted, the
invitee's assets will be transferred to the organization owner.

> POST https://[kpi]/api/v2/organizations/org_12345/invites/

- Create organization invites for registered and unregistered users.
- Set the role for which the user is being invited - (Choices: `member`,
`admin`). Default is `member`.

Payload:
```
{
    "invitees": ["demo14", "[email protected]", "[email protected]"],
    "role": "admin"
}
```

Response:
```
[
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "demo14"
    },
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/6746121a-7a87-4c2d-9994-85e38d8cff65/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "demo13"
    },
    {
        "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/2af706a9-4f67-4145-a0d3-8d66fbc77a19/",
        "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
        "status": "pending",
        "invitee_role": "admin",
        "created": "2025-01-06T13:01:40Z",
        "modified": "2025-01-06T13:01:40Z",
        "invitee": "[email protected]"
    }
]
```

> GET https://[kpi]/api/v2/organizations/org_12345/invites/

Response:
```
{
    "count": 3,
    "next": null,
    "previous": null,
    "results": [
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/6746121a-7a87-4c2d-9994-85e38d8cff65/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "demo13"
        },
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "demo14"
        },
        {
            "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/2af706a9-4f67-4145-a0d3-8d66fbc77a19/",
            "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
            "status": "pending",
            "invitee_role": "admin",
            "created": "2025-01-06T13:01:40Z",
            "modified": "2025-01-06T13:01:40Z",
            "invitee": "[email protected]"
        }
    ]
}
```

> PATCH
https://[kpi]/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/

- Update an organization invite to accept, decline, cancel, expire, or
resend.

Payload:
```
{
    "status": "accepted"
}
```

Response:
```
{
    "url": "http://kf.kobo.local/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/",
    "invited_by": "http://kf.kobo.local/api/v2/users/gtl_raj/",
    "status": "accepted",
    "invitee_role": "admin",
    "created": "2025-01-06T13:01:40Z",
    "modified": "2025-01-06T13:01:40Z",
    "invitee": "demo14"
}
```

> DELETE
https://[kpi]/api/v2/organizations/org_12345/invites/7a4f9a3b-9112-43cc-a6ae-bb4a6583b4b2/

- Organization owner or admin can delete an organization invite.

Response: 204

---------

Co-authored-by: Olivier Leger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants